'use strict';

const Controller = require('./base');
const fs = require('fs');
const path = require('path');
const childProcess = require('child_process');
const { v4: uuidv4 } = require('uuid');
const { notePath, ignoreDirs } = require('../utils/getPath');

class RefreshController extends Controller {
  async run() {
    const { ctx } = this;
    const ws = ctx.websocket;

    ws.on('message', async msg => {
      const data = JSON.parse(msg.toString());
      if (data.type === '__REFRESH__') {
        await this.gitPull();
        await this.deleteAllCategory();
        await this.addAllCategory();
        const sortCategory = await this.getSortCategory();
        await this.deleteAllArticle();
        await this.loadAllArticle(sortCategory);
      }
    });
  }

  // 拉取最新的笔记
  async gitPull() {
    const gitPull = childProcess.execSync('git pull', {
      cwd: notePath,
    });
    this.ctx.websocket.send(gitPull.toString());
  }

  // 删除 elasticSearch 中的 category 文档
  async deleteAllCategory() {
    const { ctx } = this;
    const result = await ctx.service.category.deleteAll();
    ctx.websocket.send(`delete all category ${result ? 'success' : 'error'}`);
  }

  // 遍历笔记目录，添加所有 category 文档
  async addAllCategory() {
    const { ctx } = this;
    const ws = ctx.websocket;

    ws.send('load category start');

    const data = [];
    const firstDir = fs.readdirSync(notePath);
    let firstSort = 1;
    for (const firstDirName of firstDir) {
      if (ignoreDirs.includes(firstDirName)) continue;

      const firstText = firstDirName.split(' ')[1];
      const firstId = uuidv4();
      data.push({
        id: firstId,
        text: firstText,
        sort: firstSort++,
        parent_id: '0',
      });

      const firstDirPath = path.join(notePath, firstDirName);
      const secondDir = fs.readdirSync(firstDirPath);
      let secondSort = 1;
      for (const secondDirName of secondDir) {
        if (ignoreDirs.includes(secondDirName)) continue;

        const split = secondDirName.split(' ');
        const secondText = split[split.length - 1];
        data.push({
          id: uuidv4(),
          text: secondText,
          sort: secondSort++,
          parent_id: firstId,
        });
      }
    }

    ws.send('load category end, load count is ' + data.length);

    ws.send('add category to elasticsearch start');

    const result = await ctx.service.category.bulkInsert(data);
    if (result.errors.length) {
      result.errors.forEach(item => ws.send(item));
    }
    ws.send('add category to elasticsearch end, the count is ' + result.count);
  }

  // 从 elasticSearch 中获取所有排序后的 category 文档，并整理成以下格式
  /**
   * interface Props {
   *   [key: string]: {
   *     id: string
   *     children: Props
   *   }
   * }
   */
  async getSortCategory() {
    const { ctx } = this;
    const ws = ctx.websocket;

    ws.send('get sort category start');

    const textMapId = {};
    const result = await ctx.service.category.selectOrder();

    const idMapText = {};
    result.forEach(item => {
      const body = item._source;
      idMapText[body.id] = body.text;
      if (body.parent_id === '0') {
        textMapId[body.text] = { id: body.id, children: {} };
      } else {
        textMapId[ idMapText[body.parent_id] ].children[body.text] = body.id;
      }
    });

    ws.send('get sort category end');
    return textMapId;
  }

  // 删除所有 elasticSearch 中的 article 文档
  async deleteAllArticle() {
    const { ctx } = this;
    const ws = ctx.websocket;

    ws.send('delete all article start');
    await ctx.service.article.deleteAll();
    ws.send('delete all article end');
  }

  // 遍历笔记目录，返回一下格式的数据
  // 插入数据库
  async loadAllArticle(category) {
    const { ctx } = this;
    const ws = ctx.websocket;

    ws.send('load all article start');
    const data = [];

    const firstDir = fs.readdirSync(notePath);
    for (const firstDirName of firstDir) {
      if (ignoreDirs.includes(firstDirName)) continue;

      const firstText = firstDirName.split(' ')[1];
      const firstCategoryId = category[firstText].id;

      const firstDirPath = path.join(notePath, firstDirName);
      const secondDir = fs.readdirSync(firstDirPath);
      for (const secondDirName of secondDir) {
        if (ignoreDirs.includes(secondDirName)) continue;

        const split = secondDirName.split(' ');
        const secondText = split[split.length - 1];
        const secondCategoryId = category[firstText].children[secondText];

        const secondDirPath = path.join(firstDirPath, secondDirName);
        const mdFiles = fs.readdirSync(secondDirPath);
        mdFiles.forEach((mdFileName, index) => {
          if (path.extname(mdFileName) === '.md') {
            const filePath = path.join(secondDirPath, mdFileName);

            const obj = {
              id: uuidv4(),
              title: path.basename(filePath, '.md'),
              sort: index + 1,
              first_category: firstCategoryId,
              second_category: secondCategoryId,
            };

            const stats = fs.statSync(filePath);
            obj.time = new Date(stats.mtime).getTime();

            let content = fs.readFileSync(filePath);

            if (content[0] === 0xEF && content[1] === 0xBB && content[2] === 0xBF) {
              content = content.slice(3);
            }
            obj.content = content.toString('utf-8');

            data.push(obj);
          }
        });
      }
    }

    ws.send('load all article end, the count is ' + data.length);

    ws.send('add all article to elasticsearch start');
    const result = await ctx.service.article.bulkInsert(data);
    if (result.errors.length) {
      result.errors.forEach(item => ws.send(item));
    }
    ws.send('add all article to elasticsearch end, the count is ' + result.count);
  }
}

module.exports = RefreshController;
